const util = require('clf-util');
const BBBOSS = require('./bbb-oss');
const debug = require('debug')('bbb-oss:record');
const path = require('path');
const mkdirp = require('mkdirp');
const fs = require('fs');
const defaultConf = {
	compressDir: '/var/bigbluebutton/published/presentation',
	originDir: '/var/bigbluebutton/recording/raw'
};
const _ = require('lodash');
const md5 = require('md5');
const asy = require('async');
const StorageStatus = require('./model/StorageStatus')
module.exports = class BBBRecord {
	constructor(connectionStr, options) {
		this.options = _.defaultsDeep({}, options, defaultConf);
		this.oss = new BBBOSS(connectionStr, options);
	}

	/**
	 * 获取metadata.xml文件
	 * @param meetingID
	 * @return {Promise<*>}
	 */
	async getMetadata(meetingID) {
		debug('getMetadata--meetingID:%s', meetingID);
		let filename = BBBOSS.joinPath(meetingID, 'metadata.xml');
		return this.oss.pull(filename);
	}

	/**
	 * 检查文件是否需要解冻
	 * @param filename
	 * @return {Promise<void>}
	 */
	async needRestore(filename) {
		return this.oss.head(filename).then(r => StorageStatus.isStorage(r.res.headers['x-oss-storage-class']));
	}

	/**
	 * 解冻文件，解冻完成后才返回
	 * @param filename
	 * @param time
	 * @return {Promise<T>}
	 */
	async restoreArchive(filename, time = 60) {
		const that = this;
		return new Promise(function(res, rej) {
			return that.oss.restore(filename).then(() => {
				setTimeout(() => that.restoreArchive(filename, time).then(res).catch(rej), time * 1000);
			}).catch(r => {
				switch (r.status) {
					case 200:
					case 400:
						res(filename);
						break;
					case 409:
						time = time > 10 ? time / 2 : 10;
						setTimeout(() => that.restoreArchive(filename, time).then(res).catch(rej), time * 1000);
						break;
					default:
						debug('restoreObject:%o', r);
						rej(r.name);
				}
			})
		});
	}

	/**
	 * 解冻所有文件，成功返回true，错误返回第一个错误文件名
	 * @param meetingId
	 * @param opts
	 * @return {Promise<*>}
	 */
	async restoreMeetingArchive(meetingId, opts) {
		let files = await this.oss.pullRecordingAll(meetingId, opts);
		if (!files.length) {
			debug('未找到文件目录：%s', meetingId);
			return 0;
		}
		return asy.every(files, (file, cb) => {
			this.restoreArchive(file.name).then(() => cb(null, file)).catch(cb);
		});
	}

	/**
	 * 解冻并下载文件
	 * @param meetingId
	 * @param parentDir
	 * @param opts
	 * @return {Promise<*>}
	 */
	async restoreAndSave(meetingId, parentDir, opts) {
		return this.restoreAndSaveEx(meetingId, parentDir, opts)
		// let files = await this.oss.pullRecordingAll(meetingId, opts);
		// if (!files.length) {
		//     debug('未找到文件目录：%s', meetingId);
		//     return 0;
		// }
		// const restoreFiles = await asy.filter(files, (file, cb) => {
		//     cb(null, restoreClass.indexOf(file.storageClass) >= 0);
		// });
		// debug('总共文件：%s，需要解冻文件：%s', files.length, restoreFiles.length);
		// try {
		//     await asy.every(files, (file, cb) => {
		//         this.restoreArchive(this.removePrefix(file.name)).then(() => cb(null, file)).catch(cb);
		//     });
		// } catch (e) {
		//     throw "文件解冻失败：" + e;
		// }
		// for (let file of files) {
		//    await this.saveFile(file.name, file.size, file.etag, parentDir);
		// }
		// return files;
	}

	async restoreAndSaveEx(meetingId, parentDir, opts) {
		let files = await this.oss.pullRecordingAll(meetingId, opts);
		if (!files.length) {
			debug('未找到文件目录：%s', meetingId);
			return 0;
		}
		let groups = _.groupBy(files, (file) => {
			return StorageStatus.isStorage(file.storageClass)
		})
		let storage = groups['true'] ? groups['true'] : []
		let normal = groups['false'] ? groups['false'] : []
		try {
			for (let file of storage) {
				let filename = this.removePrefix(file.name)
				let restored = await this.oss.isRestored(filename)
				if (!restored) {
					await this.oss.restore(filename)
				}
			}
		} catch (e) {
			throw "文件解冻失败：" + e.message;
		}
		try {
			for (let file of normal) {
				await this.saveFile(file.name, file.size, file.etag, parentDir);
			}
			for (let file of storage) {
				await this.oss.restoreEx(this.removePrefix(file.name)).then((fileName => this.saveFile(file.name, file.size, file.etag, parentDir)))
			}
		} catch (e) {
			throw "文件下载失败：" + e;
		}
		return files;
	}

	/**
	 * 将文件保存到本地
	 * @param meetingID 房间id
	 * @param parentDir 文件保存到的目录
	 * @param opts 选项
	 * @return {Promise<void>}
	 */
	async saveRecording(meetingId, parentDir, opts) {
		return this.restoreAndSaveEx(meetingId, parentDir, opts)
		// let files = await this.oss.pullRecordingAll(meetingID, opts);
		// if (!files.length) {
		//     debug('未找到文件目录：%s', meetingID);
		//     return 0;
		// }
		// for (let file of files) {
		//   await  this.saveFile(file.name, file.size, file.etag, parentDir);
		// }
		// return files;
	}

	/**
	 * 保存文件到本地，会检验文件的内容是否有修改，如果存在没有修改则跳过下载操作
	 * @param filename
	 * @param size
	 * @param etag
	 * @param parentDir
	 * @return {Promise<boolean>}
	 */
	async saveFile(filename, size, etag, parentDir) {
		return new Promise((resolve, reject) => {
			let filepath = this.resolvePath(parentDir, filename);
			this.mkdirp(filepath);
			debug('saveFile--filepath:%s', filepath);
			let archivePath = this.removePrefix(filename);// filename.replace(this.options.prefix, '');
			try {
				// todo 文件怎么校验是否有修改
				let stat = fs.statSync(filepath);
				if (stat.size > 1024 * 1024) {
					if (stat.size === size) {
						return resolve();
					}
				} else {
					let content = fs.readFileSync(filepath);
					let etag = md5(content).toUpperCase();
					if (etag === etag && stat.size === size) {
						return resolve();
					}
				}
			} catch (e) {

			}
			this.oss.pull(archivePath).then(filestream => {
				let w = fs.createWriteStream(filepath);
				filestream.stream.pipe(w);
				w.on('close', resolve);
			}).catch(reject);
		})
	}

	/**
	 * 组合本地文件路径
	 * @param parent 父目录
	 * @param name 文件名
	 * @return {string}
	 */
	resolvePath(parent, name) {
		let paths = this.oss.options.prefix ? name.substr(this.oss.options.prefix.length).split('/') : name.split('/');
		return path.resolve(parent, ...paths);
	}


	removePrefix(ossPath) {
		return this.oss.options.prefix && ossPath.indexOf(this.oss.options.prefix) === 0 ? ossPath.substr(this.oss.options.prefix.length) : ossPath;
	}

	/**
	 * 创建文件夹
	 * @param filename
	 */
	mkdirp(filename) {
		mkdirp(path.dirname(filename));
	}


};
